from django.db.models import Q
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search, Q as ES_Q
import json
from typing import List, Dict, Any
from .models import (
    User, UserActivity, CommunityPost, Video, Course, 
    SearchHotword
)
import re

SENSITIVE_WORDS = [
    '暴力', '色情', '赌博', '毒品', '枪支', '广告', '违法', '反动', '敏感', 'fuck', 'shit', 'sex', 'porn', 'gamble', 'kill', 'gun', 'ad', 'spam'
]

def is_sensitive(query):
    q = query.lower()
    for word in SENSITIVE_WORDS:
        if word in q:
            return True
    return False

def is_chinese(text):
    return re.search('[一-龥]', text) is None

class SearchEngine:
    """综合搜索引擎，支持ES和MySQL搜索"""
    
    def __init__(self):
        # 初始化ES客户端（如果ES可用）
        try:
            self.es = Elasticsearch(['http://localhost:9200'])
            self.es_available = self.es.ping()
        except:
            self.es_available = False
            self.es = None
    
    def search_all(self, query: str, limit: int = 20) -> Dict[str, Any]:
        """
        综合搜索所有相关内容
        
        Args:
            query: 搜索关键词
            limit: 返回结果数量限制
            
        Returns:
            包含各类搜索结果的字典
        """
        if is_sensitive(query):
            return {
                'users': [],
                'posts': [],
                'videos': [],
                'courses': [],
                'total_count': 0
            }
        results = {
            'users': [],
            'posts': [],
            'videos': [],
            'courses': [],
            'total_count': 0
        }
        
        # 更新搜索热词
        self._update_hotword(query)
        
        # 如果ES可用，优先使用ES搜索
        if self.es_available:
            es_results = self._search_elasticsearch(query, limit)
            results.update(es_results)
        else:
            # 回退到MySQL搜索
            mysql_results = self._search_mysql(query, limit)
            results.update(mysql_results)
        
        # 计算总结果数
        results['total_count'] = (
            len(results['users']) + len(results['posts']) + 
            len(results['videos']) + len(results['courses'])
        )
        
        return results
    
    def _search_elasticsearch(self, query: str, limit: int) -> Dict[str, List]:
        """使用Elasticsearch进行搜索"""
        if is_sensitive(query):
            return {'users': [], 'posts': [], 'videos': [], 'courses': []}
        results = {
            'users': [],
            'posts': [],
            'videos': [],
            'courses': []
        }
        
        try:
            # 搜索用户
            user_search = Search(using=self.es, index='users')
            if is_chinese(query):
                user_search = user_search.query(
                    ES_Q('match', username=query)
                )
            else:
                user_search = user_search.query(
                    ES_Q('multi_match', query=query, fields=['username', 'level'], fuzziness='AUTO')
                )
            user_search = user_search[:limit//4]
            user_response = user_search.execute()
            
            for hit in user_response:
                if hit.meta.score < 2:
                    continue
                results['users'].append({
                    'id': hit.meta.id,
                    'username': hit.username,
                    'avatar': hit.avatar,
                    'level': hit.level,
                    'type': 'user',
                    'score': hit.meta.score
                })
            
            # 搜索社区动态
            post_search = Search(using=self.es, index='community_posts')
            if is_chinese(query):
                post_search = post_search.query(
                    ES_Q('match', title=query)
                )
            else:
                post_search = post_search.query(
                    ES_Q('multi_match', query=query, fields=['title^3', 'content', 'duration', 'pace', 'calorie', 'author', 'author_level', 'author_avatar'], fuzziness='AUTO')
                )
            post_search = post_search[:limit//4]
            post_response = post_search.execute()
            
            for hit in post_response:
                if hit.meta.score < 2:
                    continue
                results['posts'].append({
                    'id': hit.meta.id,
                    'title': hit.title,
                    'content': hit.content,
                    'duration': hit.duration,
                    'pace': hit.pace,
                    'calorie': hit.calorie,
                    'user': hit.user,
                    'type': 'post',
                    'score': hit.meta.score
                })
            
            # 搜索视频
            video_search = Search(using=self.es, index='videos')
            if is_chinese(query):
                video_search = video_search.query(
                    ES_Q('match', title=query)
                )
            else:
                video_search = video_search.query(
                    ES_Q('multi_match', query=query, fields=['title^3', 'author'], fuzziness='AUTO')
                )
            video_search = video_search[:limit//4]
            video_response = video_search.execute()
            
            for hit in video_response:
                if hit.meta.score < 2:
                    continue
                results['videos'].append({
                    'id': hit.meta.id,
                    'title': hit.title,
                    'cover': hit.cover,
                    'author': hit.author,
                    'author_avatar': hit.author_avatar,
                    'duration': hit.duration,
                    'views': hit.views,
                    'time': hit.time,
                    'type': 'video',
                    'score': hit.meta.score
                })
            
            # 搜索课程
            course_search = Search(using=self.es, index='courses')
            if is_chinese(query):
                course_search = course_search.query(
                    ES_Q('match', name=query)
                )
            else:
                course_search = course_search.query(
                    ES_Q('multi_match', query=query, fields=['name^3', 'difficulty', 'part', 'property', 'feature', 'equipment'], fuzziness='AUTO')
                )
            course_search = course_search[:limit//4]
            course_response = course_search.execute()
            
            for hit in course_response:
                if hit.meta.score < 2:
                    continue
                results['courses'].append({
                    'id': hit.meta.id,
                    'name': hit.name,
                    'difficulty': hit.difficulty,
                    'part': hit.part,
                    'duration': hit.duration,
                    'property': hit.property,
                    'feature': hit.feature,
                    'equipment': hit.equipment,
                    'type': 'course',
                    'score': hit.meta.score
                })
                
        except Exception as e:
            print(f"ES搜索出错: {e}")
            # 如果ES搜索失败，回退到MySQL搜索
            mysql_results = self._search_mysql(query, limit)
            results.update(mysql_results)
        
        return results
    
    def _search_mysql(self, query: str, limit: int) -> Dict[str, List]:
        """使用MySQL进行模糊搜索"""
        if is_sensitive(query):
            return {'users': [], 'posts': [], 'videos': [], 'courses': []}
        results = {
            'users': [],
            'posts': [],
            'videos': [],
            'courses': []
        }
        
        # 构建搜索条件
        search_conditions = Q()
        for word in query.split():
            search_conditions |= Q(username__icontains=word)
        
        # 搜索用户
        users = User.objects.filter(search_conditions)[:limit//4]
        for user in users:
            results['users'].append({
                'id': user.id,
                'username': user.username,
                'avatar': user.avatar,
                'level': user.level,
                'type': 'user',
                'score': 1.0
            })
        
        # 搜索社区动态
        post_conditions = Q()
        for word in query.split():
            post_conditions |= (
                Q(title__icontains=word) | 
                Q(content__icontains=word) |
                Q(duration__icontains=word) |
                Q(pace__icontains=word) |
                Q(calorie__icontains=word)
            )
        
        posts = CommunityPost.objects.select_related('user').filter(post_conditions)[:limit//4]
        for post in posts:
            results['posts'].append({
                'id': post.id,
                'title': post.title,
                'content': post.content[:100] + '...' if len(post.content) > 100 else post.content,
                'duration': post.duration,
                'pace': post.pace,
                'calorie': post.calorie,
                'user': {
                    'username': post.user.username,
                    'avatar': post.user.avatar
                },
                'created_at': post.created_at,
                'type': 'post',
                'score': 1.0
            })
        
        # 搜索视频
        video_conditions = Q()
        for word in query.split():
            video_conditions |= (
                Q(title__icontains=word) | 
                Q(author__icontains=word)
            )
        
        videos = Video.objects.filter(video_conditions)[:limit//4]
        for video in videos:
            results['videos'].append({
                'id': video.id,
                'title': video.title,
                'cover': video.cover,
                'author': video.author,
                'author_avatar': video.author_avatar,
                'duration': video.duration,
                'views': video.views,
                'time': video.time,
                'type': 'video',
                'score': 1.0
            })
        
        # 搜索课程
        course_conditions = Q()
        for word in query.split():
            course_conditions |= (
                Q(name__icontains=word) | 
                Q(difficulty__icontains=word) |
                Q(part__icontains=word) |
                Q(property__icontains=word) |
                Q(feature__icontains=word) |
                Q(equipment__icontains=word)
            )
        
        courses = Course.objects.filter(course_conditions)[:limit//4]
        for course in courses:
            results['courses'].append({
                'id': course.id,
                'name': course.name,
                'difficulty': course.difficulty,
                'part': course.part,
                'duration': course.duration,
                'property': course.property,
                'feature': course.feature,
                'equipment': course.equipment,
                'type': 'course',
                'score': 1.0
            })
        
        return results
    
    def _update_hotword(self, query: str):
        """更新搜索热词统计"""
        try:
            hotword, created = SearchHotword.objects.get_or_create(word=query)
            hotword.count += 1
            hotword.save()
        except Exception as e:
            print(f"更新热词失败: {e}")
    
    def get_hotwords(self, limit: int = 10) -> List[str]:
        """获取搜索热词"""
        try:
            hotwords = SearchHotword.objects.order_by('-count')[:limit]
            return [hw.word for hw in hotwords]
        except Exception as e:
            print(f"获取热词失败: {e}")
            return []
    
    def get_suggestions(self, query: str, limit: int = 5) -> List[str]:
        """获取搜索建议"""
        suggestions = []
        
        # 从用户中获取建议
        users = User.objects.filter(username__icontains=query)[:limit//3]
        suggestions.extend([user.username for user in users])
        
        # 从课程中获取建议
        courses = Course.objects.filter(name__icontains=query)[:limit//3]
        suggestions.extend([course.name for course in courses])
        
        # 从视频中获取建议
        videos = Video.objects.filter(title__icontains=query)[:limit//3]
        suggestions.extend([video.title for video in videos])
        
        return list(set(suggestions))[:limit]

    def es_search(self, query: str, category: str = None, sort: str = 'relevance', filters: dict = None, limit: int = 20, page: int = 1) -> dict:
        """
        用ES真实索引结构进行搜索，支持分类、排序、筛选、分页
        """
        index_map = {
            'users': {
                'index': 'users',
                'fields': ['username']
            },
            'posts': {
                'index': 'community_posts_hot',
                'fields': [
                    'title', 'content', 'duration', 'pace', 'calorie',
                    'user_username', 'user_avatar'  # 只保留字符串类型字段，支持昵称搜索
                ]
            },
            'videos': {
                'index': 'videos',
                'fields': ['title', 'author']
            },
            'courses': {
                'index': 'courses',
                'fields': ['name', 'difficulty', 'part', 'property', 'feature', 'equipment']
            }
        }

        results = {k: [] for k in index_map}
        indices = [category] if category in index_map else index_map.keys()

        for cat in indices:
            idx = index_map[cat]['index']
            fields = index_map[cat]['fields']
            s = Search(using=self.es, index=idx)
            # 多字段模糊匹配
            s = s.query(ES_Q('multi_match', query=query, fields=fields, fuzziness='AUTO'))
            # 筛选
            if filters:
                for fkey, fval in filters.items():
                    s = s.filter('term', **{fkey: fval})
            # 排序
            if sort == 'time':
                s = s.sort('-created_at')
            elif sort == 'relevance':
                s = s.sort('_score')
            # 分页
            start = (page - 1) * limit
            s = s[start:start+limit]
            response = s.execute()
            for hit in response:
                doc = hit.to_dict()
                doc['id'] = hit.meta.id
                doc['score'] = hit.meta.score
                results[cat].append(doc)

        # 只返回有内容的类别
        results = {k: v for k, v in results.items() if v}
        return results

    def mysql_search(self, query: str, category: str = None, sort: str = 'relevance', filters: dict = None, limit: int = 20, page: int = 1) -> dict:
        """
        用MySQL模糊搜索，支持分类、排序、筛选、分页
        """
        results = self._search_mysql(query, limit)
        # 分类过滤
        if category:
            for k in list(results.keys()):
                if k != category and k != 'total_count':
                    results[k] = []
        # 排序
        if sort == 'time':
            for k in results:
                if isinstance(results[k], list):
                    results[k] = sorted(results[k], key=lambda x: x.get('created_at', ''), reverse=True)
        # 筛选
        if filters:
            for k in results:
                if isinstance(results[k], list):
                    for fkey, fval in filters.items():
                        results[k] = [item for item in results[k] if str(item.get(fkey, '')) == str(fval)]
        # 分页
        for k in results:
            if isinstance(results[k], list):
                start = (page-1)*limit
                end = start+limit
                results[k] = results[k][start:end]
        return results 